// PHZ
// 2018-5-15

#include "Pipe.h"
#include <random>
#include <string>
#include <array>


Pipe::Pipe()
{

}

Pipe::~Pipe()
{
	Close();
}

void SetNonBlock(SOCKET fd)
{
#if defined(__linux) || defined(__linux__) 
	int flags = fcntl(fd, F_GETFL, 0);
	fcntl(fd, F_SETFL, flags | O_NONBLOCK);
#else
	unsigned long on = 1;
	ioctlsocket(fd, FIONBIO, &on);
#endif
}

void SetBlock(SOCKET fd, int write_timeout)
{
#if defined(__linux) || defined(__linux__) 
	int flags = fcntl(fd, F_GETFL, 0);
	fcntl(fd, F_SETFL, flags & (~O_NONBLOCK));
#elif defined(WIN32) || defined(_WIN32)
	unsigned long on = 0;
	ioctlsocket(fd, FIONBIO, &on);
#else
#endif
	if (write_timeout > 0)
	{
#ifdef SO_SNDTIMEO
#if defined(__linux) || defined(__linux__) 
		struct timeval tv = { write_timeout / 1000, (write_timeout % 1000) * 1000 };
		setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char*)&tv, sizeof tv);
#elif defined(WIN32) || defined(_WIN32)
		unsigned long ms = (unsigned long)write_timeout;
		setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char*)&ms, sizeof(unsigned long));
#else
#endif		
#endif
	}
}

bool Pipe::Create()
{
#if defined(WIN32) || defined(_WIN32) 
	//AfTcpSocket rp(socket(AF_INET, SOCK_STREAM, 0)), wp(socket(AF_INET, SOCK_STREAM, 0));
	AfTcpSocket rp;
	AfTcpSocket wp;
	int ret = 0;

	std::random_device rd;
	uint16_t port = 0;
	int again = 5;

	while(again--) {
		port = rd(); 
		if (rp.Open(AfSockAddr("127.0.0.1", port), true) == 0) {
			break;
		}		
	}

	if (again == 0) {
		return false;
	}
    
	if (rp.Listen(1) < 0) {
		return false;
	}
	if (0 != wp.Open())
	{
		return false;
	}
	if (0 != wp.Connect(AfSockAddr("127.0.0.1", port))) {
		return false;
	}
	pipe_fd_[1] = wp.hSock;
	AfTcpSocket p;
	ret = rp.Accept(&p);
	if (ret < 0) {
		return false;
	}
	pipe_fd_[0] = p.hSock;

	SetNonBlock(pipe_fd_[0]);
	SetNonBlock(pipe_fd_[1]);
#elif defined(__linux) || defined(__linux__) 
	if (pipe2(pipe_fd_, O_NONBLOCK | O_CLOEXEC) < 0) {
		return false;
	}
#endif
	return true;
}

int Pipe::Write(void *buf, int len)
{
#if defined(WIN32) || defined(_WIN32) 
    return ::send(pipe_fd_[1], (char *)buf, len, 0);
#elif defined(__linux) || defined(__linux__) 
    return ::write(pipe_fd_[1], buf, len);
#endif 
}

int Pipe::Read(void *buf, int len)
{
#if defined(WIN32) || defined(_WIN32) 
    return recv(pipe_fd_[0], (char *)buf, len, 0);
#elif defined(__linux) || defined(__linux__) 
    return ::read(pipe_fd_[0], buf, len);
#endif 
}

void Pipe::Close()
{
#if defined(WIN32) || defined(_WIN32) 
	closesocket(pipe_fd_[0]);
	closesocket(pipe_fd_[1]);
#elif defined(__linux) || defined(__linux__) 
	::close(pipe_fd_[0]);
	::close(pipe_fd_[1]);
#endif

}
